package cn.sdc.sfw.dao.model;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.Time;
import java.sql.Timestamp;

import cn.sdc.sfw.exception.ReflectException;
import cn.sdc.sfw.util.ConstructorUtil;
import cn.sdc.sfw.util.MethodUtil;
import cn.sdc.sfw.util.TypeUtil;

public class Property {

	private String propertyName_;
	private Class propertyType_;
	private Method readMethod_;
	private Method writeMethod_;
	private Bean bean_;
	private Constructor stringConstructor_;
	
	public Property(String propertyName, Class propertyType,
			Method readMethod, Method writeMethod, Bean bean) {
				
		propertyName_ = propertyName;
		propertyType_ = propertyType;
		readMethod_ = readMethod;
		writeMethod_ = writeMethod;
		bean_ = bean;
		setupStringConstructor();
	}
	
	private void setupStringConstructor() {
		Constructor[] cons = propertyType_.getConstructors();
		for (int i = 0; i < cons.length; ++i) {
			Constructor con = cons[i];
			if (con.getParameterTypes().length == 1 && con.getParameterTypes()[0].equals(String.class)) {
				stringConstructor_ = con;
				break;
			}
		}
	}
	
	public final String getPropertyName() {
		return propertyName_;
	}

	public final Class getPropertyType() {
		return propertyType_;
	}
	
	public final Method getReadMethod() {
		return readMethod_;
	}
	
	public final void setReadMethod(Method readMethod) {
		readMethod_ = readMethod;
	}
	
	public final boolean hasReadMethod() {
		return readMethod_ != null;
	}
	
	public final Method getWriteMethod() {
		return writeMethod_;
	}
	
	public final void setWriteMethod(Method writeMethod) {
		writeMethod_ = writeMethod;
	}
	
	public final boolean hasWriteMethod() {
		return writeMethod_ != null;
	}
	
	public final Object getValue(Object target) {
		return MethodUtil.invoke(readMethod_, target, null);
	}
	
	public final void setValue(Object target, Object value) {
		try {
			MethodUtil.invoke(writeMethod_, target, new Object[]{
				convertIfNeed(value)});
		} catch (Throwable t) {
			throw new ReflectException(ReflectException.METHOD_INVOKE_ERR,new Object[]{bean_.getBeanClass(), propertyName_},t);
		}
	}
	
	public final Bean getBean() {
		return bean_;
	}
	
	public final String toString() {
		StringBuffer buf = new StringBuffer();
		buf.append("propertyName=");
		buf.append(propertyName_);
		buf.append(",propertyType=");
		buf.append(propertyType_.getName());
		buf.append(",readMethod=");
		buf.append(readMethod_ != null ?readMethod_.getName() : "null");
		buf.append(",writeMethod=");
		buf.append(writeMethod_ != null ?writeMethod_.getName() : "null");
		return buf.toString();
	}
	
	public Object convertIfNeed(Object arg) {
		if (propertyType_.isPrimitive()) {
			return convertPrimitiveNumber(arg);
		} else if (Number.class.isAssignableFrom(propertyType_)) {
			return convertNumber(arg);
		} else if (java.util.Date.class.isAssignableFrom(propertyType_)) {
			return convertDate(arg);
		} else if (Boolean.class.isAssignableFrom(propertyType_)) {
			return TypeUtil.toBoolean(arg);
		} else if (arg instanceof String && !String.class.equals(propertyType_)) {
			return convertWithStringConstructor(arg);
		}
		return arg;
	}

	private Object convertPrimitiveNumber(Object arg) {
		return TypeUtil.convertPrimitiveNumber(propertyType_, arg);
	}
	
	private Object convertNumber(Object arg) {
		return TypeUtil.convertNumber(propertyType_, arg);
	}
	
	private Object convertDate(Object arg) {
		if (propertyType_ == java.util.Date.class) {
			return TypeUtil.toDate(arg);
		} else if (propertyType_ == Timestamp.class) {
			return TypeUtil.toTimestamp(arg);
		} else if (propertyType_ == java.sql.Date.class) {
			return TypeUtil.toSqlDate(arg);
		} else if (propertyType_ == Time.class) {
			return TypeUtil.toTime(arg);
		}
		return arg;
	}
	
	private Object convertWithStringConstructor(Object arg) {
		if (stringConstructor_ == null || arg == null) {
			return arg;
		}
		return ConstructorUtil.newInstance(stringConstructor_, new Object[]{arg});
	}
	
}
